﻿#define _CRT_SECURE_NO_WARNINGS 1

#include "PageCache.h"

PageCache PageCache::_sInst;

//获取从对象到span的映射
Span* PageCache::MapObjectToSpan(void* obj)
{
	PAGE_ID id = ((PAGE_ID)obj >> PAGE_SHIFT);

	/*std::unique_lock<std::mutex> lock(_pageMtx);
	auto ret = _idSpanMap.find(id);
	if (ret != _idSpanMap.end())
	{
		return ret->second;
	}
	else
	{
		assert(false);
		return nullptr;
	}*/

	auto ret = (Span*)_idSpanMap.get(id);
	assert(ret != nullptr);
	return ret;
}

// 释放空闲span回到Pagecache，并合并相邻的span
// central调用此函数  将span还回来
// 同时 执行逻辑 合并
// 怎么合？ 找前找后 往前找 需要 id-1 的 span（上一个span）的首尾页号  往后找 需要id+n的span（下一个span）的页号  所以我们就要记录这些span和页号的映射关系
// 先往前 1有 2未在使用 3不超过128
void PageCache::ReleaseSpanToPageCache(Span* span)
{
	if (span->_n > NP-1)
	{
		void* ptr = (void*)(span->_pageid << PAGE_SHIFT);
		SystemFree(ptr);
		_spanPool.Delete(span);

		return;
	}//大于128页的span 直接还给系统
	//向前合并
	while (1)
	{
		//找上个页所在的span
		PAGE_ID previd = span->_pageid - 1;
		//auto ret = _idSpanMap.find(previd);
		//if (ret == _idSpanMap.end())
		//	break;

		auto ret = (Span*)_idSpanMap.get(previd);
		//有没有
		if(ret == nullptr)
		    break;

		//Span* prevSpan = ret->second;
		Span* prevSpan = ret;
		//用没用
		if (prevSpan->isUse == true)
			break;
		//合并完会不会太大？
		if (prevSpan->_n + span->_n > NP - 1)
			break;

		//ok 可以合并 怎么合？  更新页号  页数
		span->_pageid = prevSpan->_pageid;
		span->_n += prevSpan->_n;

		//善后
		_spanlist[prevSpan->_n].Erase(prevSpan);
		//delete prevSpan;
		_spanPool.Delete(prevSpan);
	}

	//向后合并
	while (1)
	{
		PAGE_ID nextid = span->_pageid + span->_n;
		/*auto ret = _idSpanMap.find(nextid);
		if (ret == _idSpanMap.end())
			break;*/

		auto ret = (Span*)_idSpanMap.get(nextid);
		if (ret == nullptr)
			break;
		//Span* nextSpan = ret->second;
		Span* nextSpan = ret;

		if (nextSpan->isUse == true)
			break;
		if (nextSpan->_n + span->_n > NP - 1)
			break;

		//ok 可以合并 怎么合？
		//span->_pageid = nextSpan->_pageid;
		span->_n += nextSpan->_n;

		_spanlist[nextSpan->_n].Erase(nextSpan);
		//delete prevSpan;
		_spanPool.Delete(nextSpan);
	}
	
	_spanlist[span->_n].PushFront(span);
	span->isUse = false;
	//_idSpanMap[span->_pageid] = span;
	//_idSpanMap[span->_pageid + span->_n - 1] = span;

	_idSpanMap.set(span->_pageid, span);
	_idSpanMap.set(span->_pageid + span->_n - 1, span);

}

// 获取⼀个K⻚的span
Span* PageCache::NewSpan(size_t k)
{
	assert(k > 0);
	//大于128
	if (k > NP - 1)
	{
		void* ptr = SystemAlloc(k);
		Span* span = _spanPool.New();
		span->_pageid = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;
		//_idSpanMap[span->_pageid] = span;
		_idSpanMap.set(span->_pageid, span);

		return span;
	}
	//1 当前桶是否为空  非空拿一个span直接返回
	if (!_spanlist[k].Empty())
	{
		Span* kSpan = _spanlist[k].PopFront();

		// 建立id和span的映射，方便central cache回收小块内存时，查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_n; ++i)
		{
			//_idSpanMap[kSpan->_pageid + i] = kSpan;
			_idSpanMap.set(kSpan->_pageid+i, kSpan);
		}

		return kSpan;
		//return _spanlist[k].PopFront();
	}

	//2 查看后面桶里有没有 有就切
	//怎么查看？ 往后走 走到128为止
	// 怎么切？ 从桶里头部拿一个一个span ，然后获取该span起始页号，按起始页号算  拿出一部分  更新映射  重新插入
	for (size_t i = k + 1; i < NP; ++i)
	{
		if (!_spanlist[i].Empty())
		{
			Span* nspan = _spanlist[i].PopFront();
			//Span* kspan = new Span;
			Span* kspan = _spanPool.New();
			//地址？
			kspan->_pageid = nspan->_pageid;
			kspan->_n = k;

			nspan->_pageid += k;
			nspan->_n -= k;

			_spanlist[nspan->_n].PushFront(nspan);

			for (PAGE_ID i = 0; i < kspan->_n; ++i)
			{
				//_idSpanMap[kspan->_pageid + i] = kspan;
				_idSpanMap.set(kspan->_pageid + i, kspan);
			}

			//_idSpanMap[nspan->_pageid] = nspan;
			//_idSpanMap[nspan->_pageid + nspan->_n - 1] = nspan;
			_idSpanMap.set(nspan->_pageid, nspan);
			_idSpanMap.set(nspan->_pageid + nspan->_n - 1, nspan);


			return kspan;
		}
	}
	//3 走到128也没有  那完蛋了 问系统要吧
	//要多少 ？ 128page
	//要来之后？ 插入
	//再调用一次newspan
	//Span* span = new Span;
	Span* span = _spanPool.New();
	void* ptr = SystemAlloc(NP - 1);
	span->_pageid = (PAGE_ID)ptr >> PAGE_SHIFT;
	span->_n = NP - 1;

	_spanlist[span->_n].PushFront(span);

	//debug记录
	/*span->isUse = false;
	_idSpanMap[span->_pageid] = span;
	_idSpanMap[span->_pageid+ span->_n -1] = span;*/

	return NewSpan(k);
}